<?php

namespace Ffzhou\HydrophisLogin\Middleware;

use Ffzhou\HydrophisLogin\Extend\FieldFlint;
use Flarum\Api\Client;
use Flarum\Api\Controller\CreateUserController;
use Flarum\Http\AccessToken;
use Flarum\Http\RememberAccessToken;
use Flarum\Http\SessionAccessToken;
use Flarum\Http\SessionAuthenticator;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AvatarUploader;
use Flarum\User\LoginProvider;
use Flarum\User\User;
use http\Exception\RuntimeException;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Arr;
use Intervention\Image\ImageManager;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class LoginMiddleware implements MiddlewareInterface
{
    /**
     * @var SettingsRepositoryInterface
     */
    protected $settings;
    /**
     * @var Dispatcher
     */
    protected $events;
    /**
     * @var Client
     */
    private $api;
    /**
     * @var AvatarUploader
     */
    protected $avatarUploader;
    /**
     * @var ServerRequestInterface
     */
    protected $request;

    public function __construct(SettingsRepositoryInterface $settings, Dispatcher $events, Client $api, AvatarUploader $avatarUploader)
    {
        $this->settings = $settings;
        $this->events = $events;
        $this->api = $api;
        $this->avatarUploader = $avatarUploader;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $routeName = $request->getAttribute('routeName');
        $this->request = $request;
        if ($routeName === 'login') {
            // 进行登陆操作
            $response = $this->login();
            if ($response) {
                return $response;
            }
        }
        else {
            // 自动登陆
            $response = $this->autoLogin();
            if ($response) {
                return $response;
            }
        }
        return $handler->handle($request);
    }

    /**
     * 自动登陆token用户
     */
    private function autoLogin() {
        // 获取自动登陆token属性
        $url = $this->settings->get('ffzhou-hydrophis-login.auto_login_api');
        $tokenAttr = $this->settings->get('ffzhou-hydrophis-login.auto_login_token_attr');
        if (empty($url) || empty($tokenAttr)) {
            return false;
        }
        // 从头信息中获取token
        $ffTokens = $this->request->getHeader($tokenAttr);
        $ffToken = count($ffTokens) ? $ffTokens[0] : '';
        if (empty($ffToken)) {
            return false;
        }

        // 设置seesiontoken键
        $session = $this->request->getAttribute('session');

        // 判断当前用户是否已登录
        $actor = $this->request->getAttribute('actor');
        if (!$actor->isGuest()) {
            // 已登录, 校验token是否相同
            if ($session->get($tokenAttr) === $ffToken) {
                return false;
            }
        }

        // 进行登陆操作
        $flint = new FieldFlint();
        $data = $flint->get($url, $ffToken);
        if (empty($data) || empty($data['data']) || empty($data['data']['id'])) {
            return false;
        }
        $flintUser = $data['data'];
        // 获取用户信息
        $user = $this->getUser($flintUser);
        if (empty($user)) {
            return false;
        }

        // 进行登陆操作
        $token =  SessionAccessToken::generate($user->id);
        $this->loginUser($token);
        $session->put($tokenAttr, $ffToken);  // 设置session
        return new RedirectResponse($this->request->getUri());
    }

    /**
     * 登陆FieldFlint用户
     * @return bool
     */
    private function login()
    {
        $body = $this->request->getParsedBody();
        $params = Arr::only($body, ['identification', 'password', 'remember']);

        // 判断参数是否正确
        $username = isset($params['identification']) ? trim($params['identification']) : '';
        $password = isset($params['password']) ? trim($params['password']) : '';
        $remember = isset($params['remember']) ? $params['remember'] : null;
        if (empty($username) || empty($password)) {
            return false;
        }

        // 登陆FieldFlint
        $url = $this->settings->get('ffzhou-hydrophis-login.login_api');
        if (empty($url)) {
            return false;
        }
        $flint = new FieldFlint();
        $data = $flint->login($url, $username, $password);
        if (empty($data) || empty($data['data']) || empty($data['data']['id'])) {
            return false;
        }
        $flintUser = $data['data'];
        // 获取用户信息
        $user = $this->getUser($flintUser);
        if (empty($user)) {
            return false;
        }

        // 执行登陆操作
        // 生成token
        $token = $remember ? RememberAccessToken::generate($user->id) : SessionAccessToken::generate($user->id);
        $this->loginUser($token);
        // 返回请求
        return new JsonResponse([
            'token' => $token->token,
            'userId' => $user->id
        ]);
    }

    /**
     * 根据token进行登陆操作
     * @param $token
     * @return
     */
    private function loginUser(AccessToken $token) {
        $session = $this->request->getAttribute('session');
        if ($token !== null) {
            resolve(SessionAuthenticator::class)->logIn($session, $token);
        }
        return true;
    }

    /**
     * 获取用户信息
     * @param array $flintUser
     * @return User
     */
    private function getUser(array $flintUser): User {
        $actor = $this->request->getAttribute('actor');

        // 实例化字段
        $provider = FieldFlint::LOGIN_PROVIDER;
        $identifier = $flintUser['id'];
        $username = $flintUser['username'];
        $nickname = $flintUser['nickname'];
        $email = $flintUser['info']['info'];
        $avatarUrl = $flintUser['file_path'];

        // 判断用户是否存在
        $user = LoginProvider::logIn($provider, $identifier);
        if (!$user) {
            // 根据邮箱判断用户是否存在
            if ($user = User::where(compact('email'))->first()) {
                $user->loginProviders()->create(compact('provider', 'identifier'));
            }
            else {
                // 判断用户名是否已存在
                $count = User::where(compact('username'))->count();
                if ($count > 0) {
                    $username .= $this->getRandstr(3);
                }

                // 注册新用户
                // $username = $this->getRandstr(8);  // 生成随机用户名
                $password = substr(md5(uniqid()), 0, 8);  // 生成随机密码
                try {
                    $body = ['data' => ['attributes' => [
                        'username' => $username,
                        'email' => $email,
                        'password' => $password,
                        'isEmailConfirmed' => true,
                    ]]];
                    $response = $this->api->send(CreateUserController::class, $actor, [], $body);
                    $body = json_decode($response->getBody());
                } catch (\Exception $e) {
                    throw new RuntimeException("Error: ". $e->getMessage());
                }

                // 更新用户信息
                $id = $body->data->id; // 获取用户ID
                $user = User::where(compact('id'))->first();
                $user->activate();  // 确认邮箱
                // 上传头像
                if ($avatarUrl) {
                    $this->uploadAvatarFromUrl($user, $avatarUrl);
                }
                $user->save();  // 存储信息

                // 创建第三方标识
                $user->loginProviders()->create(compact('provider', 'identifier'));
            }
        }

        // 判断是否修改用户邮箱
        if ($email !== $user->email) {
            $user->changeEmail($email);
            $user->save();
        }

        return $user;
    }

    /**
     * 上传用户头像
     * @param User $user
     * @param string $url
     */
    private function uploadAvatarFromUrl(User $user, string $url)
    {
        $image = (new ImageManager)->make($url);
        $this->avatarUploader->upload($user, $image);
    }

    /**
     * 获取随机字符串
     * @param $length
     * @return false|string
     */
    private function getRandstr($length){
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
        $randStr = str_shuffle($str);//打乱字符串
        $rands= substr($randStr, 0, $length);//substr(string,start,length);返回字符串的一部分
        return $rands;
    }
}
